﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Cci.MutableCodeModel;

namespace Microsoft.Cci.Contrib.Mutators
{
    public class ForEachStatementMutator : MethodBodyCodeMutator
    {
        public ForEachStatementMutator(IMetadataReaderHost host)
            : base(host)
        {
        }

        public override IBlockStatement Visit(BlockStatement blockStatement)
        {
            var xBlock = base.Visit(blockStatement);
            if (xBlock.Statements.Count() < 2)
            {
                return xBlock;
            }
            var xStatements = xBlock.Statements.ToList();
            for (int i = 0; i < (xStatements.Count - 2); i++)
            {
                var xExpression = xStatements[i] as IExpressionStatement;
                if (xExpression == null)
                {
                    continue;
                }
                var xAssignment = xExpression.Expression as IAssignment;
                if (xAssignment == null)
                {
                    continue;
                }
                if (!xAssignment.Type.ToString().StartsWith("System.Collections.IEnumerator")
                  && !xAssignment.Type.ToString().StartsWith("System.Collections.Generic.IEnumerator<"))
                {
                    continue;
                }
                var xMethodCall = xAssignment.Source as IMethodCall;
                if (xMethodCall == null)
                {
                    continue;
                }
                var xSpecializedMethodRef = xMethodCall.MethodToCall as SpecializedMethodReference;
                if (xSpecializedMethodRef == null)
                {
                    continue;
                }
                if (xSpecializedMethodRef.UnspecializedVersion.ToString() != "System.Collections.Generic.IEnumerator<!0> System.Collections.Generic.IEnumerable.GetEnumerator()")
                {
                    continue;
                }
                var xTryFinally = xStatements[i+1] as ITryCatchFinallyStatement;
                if (xTryFinally == null)
                {
                    continue;
                }
                var xTryBodyStatements = xTryFinally.TryBody.Statements.ToList();
                var xExpression2 = xTryBodyStatements[2] as IExpressionStatement;
                if (xExpression2 == null)
                {
                    continue;
                }
                var xAssignment2 = xExpression2.Expression as IAssignment;
                if (xAssignment2 == null)
                {
                    continue;
                }
                var xMethodCall2 = xAssignment2.Source as IMethodCall;
                if (xMethodCall2 == null)
                {
                    continue;
                }
                var xSpecializedMethodRef2 = xMethodCall2.MethodToCall as SpecializedMethodReference;
                if (xSpecializedMethodRef2 == null)
                {
                    continue; 
                }
                if (xSpecializedMethodRef2.Name.Value != "get_Current"
                   || xSpecializedMethodRef2.UnspecializedVersion.ContainingType.ToString() != "System.Collections.Generic.IEnumerator")
                {
                    continue;
                }

                var xForEach = new ForEachStatement();
                xForEach.Collection = xMethodCall.ThisArgument;
                xForEach.Variable = (ILocalDefinition)xAssignment2.Target.Definition;
                xTryBodyStatements.RemoveRange(0, 3);
                xTryBodyStatements.RemoveRange(xTryBodyStatements.Count - 3, 3);
                var xNewBlock = new BlockStatement();
                xNewBlock.Statements.AddRange(xTryBodyStatements);
                xNewBlock.Locations.AddRange(xTryFinally.TryBody.Locations);
                xForEach.Body = xNewBlock;
                var xReturnBlock = new BlockStatement();
                xReturnBlock.Statements.AddRange(xBlock.Statements.Skip(i));
                xReturnBlock.Locations.AddRange(xBlock.Locations);
                xReturnBlock.Statements.RemoveRange(0, 2);
                xReturnBlock.Statements.Insert(0, xForEach);
                return Visit(xReturnBlock);
            }
            return xBlock;
        }
    }
}
